package com.etsy.statsd.profiler; import com.etsy.statsd.profiler.reporter.Reporter; import com.etsy.statsd.profiler.server.ProfilerServer; import com.etsy.statsd.profiler.worker.ProfilerShutdownHookWorker; import com.etsy.statsd.profiler.worker.ProfilerThreadFactory; import com.etsy.statsd.profiler.worker.ProfilerWorkerThread; import com.google.common.util.concurrent.MoreExecutors; import java.lang.instrument.Instrumentation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.atomic.AtomicReference; /** * javaagent profiler using StatsD as a backend * * @author Andrew Johnson */ public final class Agent { public static final int EXECUTOR_DELAY = 0; static AtomicReference<Boolean> isRunning = new AtomicReference<>(true); static LinkedList<String> errors = new LinkedList<>(); private Agent() { } public static void agentmain(final String args, final Instrumentation instrumentation) { premain(args, instrumentation); } /** * Start the profiler * * @param args Profiler arguments * @param instrumentation Instrumentation agent */ public static void premain(final String args, final Instrumentation instrumentation) { Arguments arguments = Arguments.parseArgs(args); Reporter reporter = instantiate(arguments.reporter, Reporter.CONSTRUCTOR_PARAM_TYPES, arguments); Collection<Profiler> profilers = new ArrayList<>(); for (Class<? extends Profiler> profiler : arguments.profilers) { profilers.add(instantiate(profiler, Profiler.CONSTRUCTOR_PARAM_TYPES, reporter, arguments)); } scheduleProfilers(profilers, arguments); registerShutdownHook(profilers); } /** * Schedule profilers with a SchedulerExecutorService * * @param profilers Collection of profilers to schedule * @param arguments */ private static void scheduleProfilers(Collection<Profiler> profilers, Arguments arguments) { // We need to convert to an ExitingScheduledExecutorService so the JVM shuts down // when the main thread finishes ScheduledExecutorService scheduledExecutorService = MoreExecutors.getExitingScheduledExecutorService( (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(profilers.size(), new ProfilerThreadFactory())); Map<String, ScheduledFuture<?>> runningProfilers = new HashMap<>(profilers.size()); Map<String, Profiler> activeProfilers = new HashMap<>(profilers.size()); for (Profiler profiler : profilers) { activeProfilers.put(profiler.getClass().getSimpleName(), profiler); ProfilerWorkerThread worker = new ProfilerWorkerThread(profiler, errors); ScheduledFuture future = scheduledExecutorService.scheduleAtFixedRate(worker, EXECUTOR_DELAY, profiler.getPeriod(), profiler.getTimeUnit()); runningProfilers.put(profiler.getClass().getSimpleName(), future); } if (arguments.httpServerEnabled) { ProfilerServer.startServer(runningProfilers, activeProfilers, arguments.httpPort, isRunning, errors); } } /** * Register a shutdown hook to flush profiler data to StatsD * * @param profilers The profilers to flush at shutdown */ private static void registerShutdownHook(Collection<Profiler> profilers) { Thread shutdownHook = new Thread(new ProfilerShutdownHookWorker(profilers, isRunning)); Runtime.getRuntime().addShutdownHook(shutdownHook); } /** * Uniformed handling of initialization exception * * @param clazz The class that could not be instantiated * @param cause The underlying exception */ private static void handleInitializationException(final Class<?> clazz, final Exception cause) { throw new RuntimeException("Unable to instantiate " + clazz.getSimpleName(), cause); } /** * Instantiate an object * * @param clazz A Class representing the type of object to instantiate * @param parameterTypes The parameter types for the constructor * @param initArgs The values to pass to the constructor * @param <T> The type of the object to instantiate * @return A new instance of type T */ private static <T> T instantiate(final Class<T> clazz, Class<?>[] parameterTypes, Object... initArgs) { try { Constructor<T> constructor = clazz.getConstructor(parameterTypes); return constructor.newInstance(initArgs); } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { handleInitializationException(clazz, e); } return null; } }